#include "GeometryModel.h"
#include "../../tools/xml/ticpp.h"
#include "../../tools/Log.h"
#include <string>

using namespace std;

void GeometryModel::GeoModelVertex::draw() {
// How to draw each vertex
	glColor4d(color.r, color.g, color.b, color.a);
	glVertex3d(pos.x, pos.y, pos.z);
}

void GeometryModel::GeoModelFace::iterateOverVerts() {
	for(std::vector<GeoModelVertex*>::iterator i = vertices.begin(); i != vertices.end(); i++)
		(*i)->draw();
}

bool GeometryModel::GeoModelFace::setMaxVertices(unsigned max) {
	if(max == (unsigned)-1 || max < vertices.size())	return false;
	
	vertices.reserve(max);
	return true;
}

// Nice and simple way to define what kind of geometry to draw
void GeometryModel::GeoModelFace::draw() {
	switch(vertices.size()) {
	case 1:
		glBegin(GL_POINTS);		break;
	case 2:
		glBegin(GL_LINES);		break;
	case 3:
		glBegin(GL_TRIANGLES);	break;
	case 4:
		glBegin(GL_QUADS);		break;
	default:
		glBegin(GL_POLYGON);	break;
	}
	
	iterateOverVerts();
	glEnd();
}

fVec3D GeometryModel::getBackLowerLeft() {
	fVec3D mins;
	bool set = false; 
	for(map<unsigned, GeoModelVertex>::iterator i = vertices.begin(); i != vertices.end(); i++) {
		if(!set) {
			set = true;
			mins = i->second.pos;
			continue;
		}
		if(i->second.pos.x < mins.x)
			mins.x = i->second.pos.x;
		if(i->second.pos.y < mins.y)
			mins.y = i->second.pos.y;
		if(i->second.pos.z < mins.z)
			mins.z = i->second.pos.z;
	}
	
	return mins * scale + localPosition;
}

fVec3D GeometryModel::getFrontUpperRight() {
	fVec3D maxs;
	bool set = false;
	for(map<unsigned, GeoModelVertex>::iterator i = vertices.begin(); i != vertices.end(); i++) {
		if(!set) {
			set = true;
			maxs = i->second.pos;
			continue;
		}
		if(i->second.pos.x > maxs.x)
			maxs.x = i->second.pos.x;
		if(i->second.pos.y > maxs.y)
			maxs.y = i->second.pos.y;
		if(i->second.pos.z > maxs.z)
			maxs.z = i->second.pos.z;
	}
	
	return maxs * scale + localPosition;
}

void GeometryModel::GeoModelFace::addVertex(GeoModelVertex* mv) {
	vertices.push_back(mv);
}

GeometryModel::GeoModelFace& GeometryModel::newFace() {
	faces.push_back(GeoModelFace());
	return faces.back();			//get the reference for the model we just created
}
	
bool GeometryModel::setMaxFaces(unsigned max) {
	if(max == (unsigned)-1 || faces.size() < max)	return false;
	faces.reserve(max);
	return true;
}
	
void GeometryModel::draw() {
	glTranslated(localPosition.x, localPosition.y, localPosition.z);
	//Leave this rotation order alone
	glRotated(localRotation.x, 1, 0, 0);
	glRotated(localRotation.y, 0, 1, 0);
	glRotated(localRotation.z, 0, 0, 1);
	glScaled(scale.x, scale.y, scale.z);
	
	for(std::vector<GeoModelFace>::iterator i = faces.begin(); i != faces.end(); i++)
		i->draw();
}
	
void GeometryModel::load(const char* filename) {
	using namespace ticpp;
	try {
		string val;
		Document doc( filename );
		doc.LoadFile();
		
		Node* node = doc.FirstChild(), *lists = NULL;
		while((lists = node->IterateChildren( lists)) != NULL) {
			lists->GetValue(&val);
			if(val == "VertexList") {
				Node *child = NULL;
				while( (child = lists->IterateChildren( child )) != NULL) {
					child->GetValue(&val);
					if(val != "vertex")
						throw Exception("VertexList element was not a vertex");
					Element* elem = child->ToElement();
					
					unsigned id;
					elem->GetAttribute("id", &id, true);
					
					GeoModelVertex& mv = vertices[id];
					elem->GetAttribute("x", &mv.pos.x, false);
					elem->GetAttribute("y", &mv.pos.y, false);
					elem->GetAttribute("z", &mv.pos.z, false);
					elem->GetAttribute("r", &mv.color.r, false);
					elem->GetAttribute("g", &mv.color.g, false);
					elem->GetAttribute("b", &mv.color.b, false);
					elem->GetAttribute("a", &mv.color.a, false);
				}
			} else if(val == "FaceList") {
				Node *child = NULL;
				while( (child = lists->IterateChildren( child )) != NULL) {
					child->GetValue(&val);
					if(val != "Face")
						throw Exception("FaceList had an element not of type Face, but "+ val);
					GeoModelFace& mf = newFace();
					
					Node* verts = NULL;
					while( (verts = child->IterateChildren( verts)) != NULL) {
						unsigned id;
						Element* elem = verts->ToElement();
						elem->GetAttribute("id", &id, true);
						if(vertices.find(id) == vertices.end())
							throw Exception("Face referenced undefined vertex " + id);
						mf.addVertex(&vertices[id]);
					}
				}
			}
			else
				throw Exception("Bad list name " + val);
		}
	}
	catch( Exception& ex )	{
	    lout <<"WARNING: GeometryModel - Error parsing \""<<filename<<"\": "<< ex.what()<<endl;
	}
}
	
	
